Implement wire protocol version compatibility semantics#113
Open
conradbzura wants to merge 7 commits intomainfrom
Open
Implement wire protocol version compatibility semantics#113conradbzura wants to merge 7 commits intomainfrom
conradbzura wants to merge 7 commits intomainfrom
Conversation
12815ea to
c4b4b25
Compare
conradbzura
commented
Feb 21, 2026
conradbzura
commented
Feb 21, 2026
conradbzura
commented
Feb 21, 2026
conradbzura
commented
Feb 21, 2026
conradbzura
commented
Feb 22, 2026
conradbzura
commented
Feb 22, 2026
conradbzura
commented
Feb 22, 2026
conradbzura
commented
Feb 22, 2026
conradbzura
commented
Feb 22, 2026
conradbzura
commented
Feb 22, 2026
conradbzura
commented
Feb 22, 2026
| local_major, | ||
| client_major, | ||
| ): | ||
| """Test dispatch yields Nack for incompatible major version. |
Contributor
Author
There was a problem hiding this comment.
Or empty or unparsable version.
c4b4b25 to
f498d1a
Compare
conradbzura
commented
Feb 22, 2026
| |<──────── Response(Result) ────| (one or more results) | ||
| |<──────── Response(Exception) ─| (on failure) | ||
| | | | ||
| ``` |
Contributor
Author
There was a problem hiding this comment.
This could be a simple Mermaid sequence diagram.
Reserve field 1 for `version` in the Task message and add TaskVersionEnvelope for pre-deserialization version extraction. Add a `version` field to Ack for observability.
Stamp wool.__version__ on outgoing Task protobuf messages and on Ack responses. Raise RpcError in WorkerConnection when a Nack is received instead of an Ack.
Add parse_major_version helper and _create_version_filter to WorkerProxy. Compose security and version filters into a single compatible predicate used across all discovery modes.
Extract the client version from raw request bytes via TaskVersionEnvelope before full deserialization. Reject requests with empty, unparseable, or incompatible major versions with a Nack.
Cover version round-tripping through protobuf, Nack handling in WorkerConnection, discovery-time major version filtering in WorkerProxy, and dispatch-time version interception in WorkerService (including empty, unparseable, and incompatible version scenarios).
Document the wire protocol, dispatch sequence, serialization strategy, version compatibility guarantees, and schema evolution rules for users of Wool.
3f765b6 to
061dea9
Compare
bb556f3 to
de0cdec
Compare
The default shallow clone fetches no tags, causing the build-hooks git version parser to fall back to "0+<hash>". This version string is unparseable by parse_major_version, failing all test_service.py tests in CI. Adding "fetch-tags: true" resolves a real tag without requiring a full history clone.
de0cdec to
bc1c4b7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add protocol version awareness to Wool's wire protocol. Incompatible workers (different major version) are filtered out during discovery before they ever reach the load balancer. As a secondary defense, a gRPC server interceptor extracts the client version from raw request bytes and rejects requests with empty, unparseable, or incompatible major versions via
Nack.A new README for the protobuf subpackage documents the dispatch wire protocol, serialization strategy, schema evolution rules, and version compatibility semantics.
Closes #102
Proposed changes
Proto schema changes
Add a
versionstring field (field 1) toTaskintask.proto, reserving it for pre-deserialization version extraction. AddTaskVersionEnvelope— a minimal message containing onlystring version = 1— for the interceptor to parse field 1 from any wire format. Add aversionstring field toAckinworker.protofor observability.Task version population
Populate
Task.versionwithwool.__version__into_protobuf(). PopulateAck(version=wool.__version__)when acknowledging a task. HandleNackresponses inWorkerConnection.dispatch()by raisingRpcError.Discovery-time version filtering
Add
parse_major_versionhelper and_create_version_filtertoWorkerProxy. Compose security and version filters into a singlecompatiblepredicate used across all discovery modes (pool URI, static workers).Dispatch-time version interception
Add
VersionInterceptor— a gRPC server interceptor that extracts the client version from raw request bytes viaTaskVersionEnvelopebefore full deserialization. Reject requests with empty, unparseable, or incompatible major versions with aNack. Wire the interceptor intoWorkerProcess.Protobuf subpackage README
Add
wool/src/wool/runtime/protobuf/README.mddocumenting the dispatch wire protocol, serialization strategy, version compatibility guarantees, and schema evolution rules — written for the Wool user persona.Test cases
TestWorkerProxyTestWorkerProxyTestWorkerProxyTestWorkerProxyTestWorkerServicewool.__version__TestWorkerServiceTestWorkerServiceTestWorkerServiceTestWorkerConnectiondispatch()is calledTestWorkerConnectiondispatch()is calledTestTaskto_protobuf()is calledwool.__version__in version fieldTestTaskImplementation plan
versionfield toTaskandTaskVersionEnvelopeintask.proto; addversionfield toAckinworker.proto; re-exportTaskVersionEnvelopein wrapperTask.versionwithwool.__version__into_protobuf(); populateAck.versioninWorkerService.dispatch(); handleNackinWorkerConnection.dispatch()parse_major_versionhelper and_create_version_filtertoWorkerProxy; compose security and version filters intocompatiblepredicateVersionInterceptorwith strict version checking (reject empty, unparseable, incompatible); wire intoWorkerProcesstest_proxy.py,test_service.py,test_connection.py, andtest_task.py(VP-001 through VP-012)